package com.study.mybatis.plugins;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

/**
 * @author yuantongqin
 * 2021/8/2
 **/
public class Plugin implements InvocationHandler {

    private final Object target;
    private final Interceptor interceptor;
    private final Map<Class<?>, Set<Method>> signatureMap;

    private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
        this.target = target;
        this.interceptor = interceptor;
        this.signatureMap = signatureMap;
    }

    /**
     * 为xxxHandler创建一个代理对象
     * 有：statementHandler
     * parameterHandler
     * resultSetHandler
     * executorHandler
     * @param target
     * @param interceptor
     */
    public static Object wrap(Object target, Interceptor interceptor) {
        Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
        Class<?> type = target.getClass();
        Class<?>[] allInterfaces = getAllInterfaces(type, signatureMap);
        if(allInterfaces.length > 0){
            return Proxy.newProxyInstance(type.getClassLoader(), allInterfaces, new Plugin(target, interceptor, signatureMap));
        }
        return target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
    }


    private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
        Intercepts annotation = interceptor.getClass().getAnnotation(Intercepts.class);
        if (annotation == null) {
            throw new PluginException(" interceptor 上必须要有@Intercepts 注解：" + interceptor.getClass().getName());
        }
        Signature[] value = annotation.value();
        Map<Class<?>, Set<Method>> result = new HashMap<>();
        for (Signature signature : value) {
            Set<Method> methods = result.computeIfAbsent(signature.type(), aClass -> new HashSet<>());
            try {
                methods.add(signature.type().getMethod(signature.method(), signature.args()));
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
        Set<Class<?>> result = new HashSet<>();
        Class<?>[] interfaces = type.getInterfaces();
        // 这里是多重代理
        while (type != null) {
            for (Class<?> anInterface : interfaces) {
                if (signatureMap.containsKey(anInterface)) {
                    result.add(anInterface);
                }
            }
            // 看父类有没有继承接口
            type = type.getSuperclass();
        }
        return result.toArray(new Class<?>[0]);
    }
}
